﻿#region MIT Licence
/*
 * Copyright (C) 2014 PdfCreative
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom 
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;

using PdfCreative.Pdf.Writer.Utilities;
using PdfCreative.Pdf.Writer.Embedding.Predictors;

namespace PdfCreative.Pdf.Writer.Embedding.Decoders
{
    /// <summary>
    /// The DeflateDecoderStream class is a decoder for data encoded with the Deflate algorithm. The DeflateDecoderStream
    /// wraps a .NET DeflateStream instance.
    /// </summary>
    [Decoder("/FlateDecode")]
    internal class DeflateDecoderStream : DecoderStream
    {
        // Private fields
        // ==============
        #region Private fields
        private const int BUFFER_SIZE = 16384;
        private byte[] _buffer;
        private int _end;
        private int _finger;
        private int _columns;
        private Predictor _predictor;
        private int _position;
        #endregion



        // Base class overrides
        // ====================
        #region Stream overrides
        /// <summary>
        /// Retrieves the current position of the stream.
        /// </summary>
        public override long Position
        {
            get
            {
                return _position;
            }
        }

        /// <summary>
        /// Reads the next available byte from the buffer. If no further bytes are available, this
        /// method returns -1.
        /// </summary>
        /// <returns>The value of the next byte.</returns>
        public override int ReadByte()
        {
            int result = 0;

            if (0 == EnsureAvailableData())
                result = -1;
            else
            {
                result = _buffer[_finger];
                _finger++;
                _position++;
            }

            return result;
        }

        /// <summary>
        /// Reads data into the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer to read data into.</param>
        /// <param name="offset">The offset  to start copying into.</param>
        /// <param name="count">The number of bytes to read.</param>
        /// <returns>The number of bytes read</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            int bytesCopied = 0;

            int bytesRead = 0;
            do
            {
                bytesRead = InternalRead(buffer, offset, count - bytesCopied);
                bytesCopied += bytesRead;
                offset += bytesRead;
            }
            while (bytesCopied < count && bytesRead > 0);

            _position += bytesCopied;
            return bytesCopied;
        }
        #endregion

        #region DeflateStream overrides
        /// <summary>
        /// Initialises the DecoderStream with the specified base stream and parameters dictionary.
        /// The parameters dictionary can be a Null Reference. This has the same effect as
        /// passing in an empty dictionary.
        /// </summary>
        /// <param name="baseStream">The base stream</param>
        /// <param name="parameters">The parameters for this decoder stream</param>
        public override void Initialise(Stream baseStream, PdfDictionary parameters)
        {
            if (null == baseStream) throw new ArgumentNullException("baseStream");

            // We find out what the column width of the source data is, and which predictor we
            // need to use. If we need a predictor, but none could be found, we cannot initialise this
            // instance.
            if (null != parameters)
            {
                _columns = ConversionUtilities.ReadInt32(parameters["/Columns"]);
                PredictorCode predictorCode = (PredictorCode)
                   ConversionUtilities.ReadInt32(parameters["/Predictor"]);

                if (PredictorCode.None != predictorCode)
                {
                    _predictor = Predictor.GetPredictor(predictorCode);
                    if (null == _predictor)
                        throw new PdfParserException(String.Format("No predictor found for function '{0}'", predictorCode));
                }
            }

            // Read away the two initial bytes
            baseStream.ReadByte();
            baseStream.ReadByte();

            // We open a .NET DeflateStream that wraps the base stream, and use that as our base stream instead.
            SetStream(new DeflateStream(baseStream, CompressionMode.Decompress));
            
        } // Initialise()

        #endregion



        // Private implementation
        // ======================
        #region Private implementation
        /// <summary>
        /// Retrieves the number of bytes available in the buffer. Where necessary, this method will
        /// attempt to re-fill the buffer to make more bytes available.
        /// </summary>
        /// <returns>The number of available bytes</returns>
        private int EnsureAvailableData()
        {
            int available = _end - _finger;

            if (0 == available)
            {
                FillBuffer();
                available = _end - _finger;
            }

            return available;
        }

        /// <summary>
        /// Fills the buffer. This reads source data into a temporary buffer, where it is transformed by the
        /// decoder's predictor function and then copied into the internal buffer.
        /// </summary>
        private void FillBuffer()
        {
            System.Diagnostics.Debug.WriteLine("% Filling deflate buffer");

            if (null == _predictor)
            {
                // If we don't use a predictor then the buffer can simply be read directly off the stream.
                _finger = 0;
                _end = Stream.Read(_buffer, 0, BUFFER_SIZE);
            }
            else
            {
                // If we are using a predictor then we need to read data into a temporary buffer, and run the
                // predictor's decoding algorithm over it. The first line of the source data is not altered.
                // If there is already some data in the buffer, the last line is copied to the temporary buffer because
                // it is required to alter the next line of raw data.

                // The raw data read from the base stream is called 'source' data; the internal buffer contains the
                // 'destination' data.
                int sourceColumns = _columns + _predictor.Padding;
                int destinationLines = BUFFER_SIZE / _columns;
                int sourceBytes = 0;
                byte[] sourceData = new byte[destinationLines * sourceColumns];
                int destinationIndex = 0;
                if (_finger > 0)
                {
                    // Copy the last data in the buffer into the source data buffer before reading it off
                    // the base stream.
                    int bytesRead = Stream.Read(sourceData, sourceColumns, sourceData.Length - sourceColumns);
                    if (bytesRead > 0)
                    {
                        Array.Copy(_buffer, _finger - _columns, sourceData, _predictor.Padding, _columns);
                        sourceBytes = bytesRead+sourceColumns;
                    }
                }
                else
                {
                    // Otherwise, read all the source data off the base stream, and then copy the first line
                    // into the internal buffer.
                    sourceBytes = Stream.Read(sourceData, 0, sourceData.Length);
                    if (sourceBytes > 0)
                    {
                        Array.Copy(sourceData, _predictor.Padding, _buffer, 0, _columns);
                        destinationIndex = _columns;
                    }
                }

                System.Diagnostics.Debug.Assert(sourceBytes % sourceColumns == 0);

                // Now, we use the predictor to decode every line in the source buffer, and copy the decoded
                // data into the internal buffer.
                int sourceFinger = sourceColumns;
                while (sourceFinger < sourceBytes)
                {
                    _predictor.Decode(sourceData, sourceFinger, sourceColumns);
                    Array.Copy(sourceData, sourceFinger + _predictor.Padding, _buffer, destinationIndex, _columns);

                    sourceFinger += sourceColumns;
                    destinationIndex += _columns;
                }

                // We reset the pointers on the internal buffer. We are now ready to read from the buffer.
                _finger = 0;
                _end = destinationIndex;
            }

            System.Diagnostics.Debug.WriteLine(String.Format("% Buffer now contains {0} bytes", _end));
            System.Diagnostics.Debug.WriteLine("% Buffer contents:");
            System.Diagnostics.Debug.WriteLine(System.Text.Encoding.ASCII.GetString(_buffer, 0, _end));

        } // FillBuffer()

        /// <summary>
        /// Reads bytes off the internal buffer into the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer</param>
        /// <param name="offset">The offset at which to start copying data into the buffer</param>
        /// <param name="count">The number of bytes to copy</param>
        /// <returns>The number of bytes read</returns>
        private int InternalRead(byte[] buffer, int offset, int count)
        {
            int bytesAvailable = EnsureAvailableData();

            int bytesRead = Math.Min(count, bytesAvailable);
            Array.Copy(_buffer, _finger, buffer, offset, bytesRead);
            _finger += bytesRead;

            return bytesRead;
        }
        #endregion



        // Public constructor
        // ==================
        #region Public constructor
        /// <summary>
        /// Constructs a new DeflateDecoderStream instance.
        /// </summary>
        public DeflateDecoderStream()
        {
            _buffer = new byte[BUFFER_SIZE];
            _finger = 0;
            _end = 0;
        }
        #endregion

    } // DeflateDecoderStream class
}
